package com.koron.rating.aop;

import cn.hutool.core.util.StrUtil;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.http.HttpServletRequest;
import java.lang.reflect.Method;
import java.util.concurrent.TimeUnit;

@Aspect
@Component
@Slf4j
public class IdempotentAspect {

    @Autowired
    private RedisTemplate redisTemplate;

    @Pointcut("@annotation(Idempotent)")
    public void pointCut() {
    }

    @Around("pointCut() && @annotation(idempotent)")
    public Object around(ProceedingJoinPoint joinPoint, Idempotent idempotent) throws Throwable {
        // 获取方法的签名信息
        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        // 获取当前方法
        Method method = signature.getMethod();
        // 获取方法参数的名称
        String[] parameterNames = signature.getParameterNames();
        // 生成key
        String key = getKey(idempotent, parameterNames);
        // 业务标识
        boolean keyExists = false;
        try {
            if (redisTemplate.hasKey(key)) {
                throw new IdempotentException(idempotent.topicMessage());
            }
            redisTemplate.opsForValue().setIfAbsent(key, method.getName(), idempotent.expired(), TimeUnit.MINUTES);

            keyExists = true;
            // 执行业务方法，如果业务方法执行完毕，标识设置为true
            return joinPoint.proceed();
        } finally {
            // 业务方法执行完之后删除key
            if (keyExists){
                redisTemplate.delete(key);
            }
        }
    }

    private String getKey(Idempotent idempotent, String[] parameterNames) {
        // 拼接Redis Key
        StringBuilder sb = new StringBuilder(idempotent.keyPrefix());

        // 获取HttpServletRequest对象
        HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
        if (parameterNames != null) {
            for (String parameterName : parameterNames) {
                if (!parameterName.equals(idempotent.key())) {
                    continue;
                }
                String parameterValue = request.getParameter(parameterName);
                if (!StrUtil.isBlank(parameterValue)) {
                    sb.append(parameterName + ":");
                    sb.append(parameterValue);
                } else {
                    sb.append(idempotent.key());
                }
            }
        }
        return sb.toString();
    }
}
